import 'dart:ui';

import 'package:math_keyboard/src/foundation/node.dart';

/// Class for handling subscript and superscript combinations.
// class TeXSubscriptSuperscript extends TeXFunction {
//   /// Constructs a [TeXSubscriptSuperscript].
//   TeXSubscriptSuperscript(
//     TeXNode parent,
//     TeXNode baseNode, {
//     TeXNode? subscriptNode,
//     TeXNode? superscriptNode,
//   })  : assert(subscriptNode != null || superscriptNode != null,
//             'At least one of subscript or superscript must be provided'),
//         super(
//           _buildExpression(subscriptNode != null, superscriptNode != null),
//           parent,
//           _buildArgs(subscriptNode != null, superscriptNode != null),
//           _buildArgNodes(subscriptNode, superscriptNode),
//         ) {
//     this.baseNode = baseNode;
//     baseNode.parent = this;
//   }
//
//   /// The base expression that the subscript/superscript applies to.
//   late final TeXNode baseNode;
//
//   /// Gets the subscript node if it exists.
//   TeXNode? get subscriptNode => hasSubscript ? argNodes[0] : null;
//
//   /// Gets the superscript node if it exists.
//   TeXNode? get superscriptNode =>
//       hasSuperscript ? argNodes[hasSubscript ? 1 : 0] : null;
//
//   /// Whether this has a subscript.
//   bool get hasSubscript => expression.contains('_');
//
//   /// Whether this has a superscript.
//   bool get hasSuperscript => expression.contains('^');
//
//   static String _buildExpression(bool hasSubscript, bool hasSuperscript) {
//     if (hasSubscript && hasSuperscript) {
//       return '_^';
//     } else if (hasSubscript) {
//       return '_';
//     } else {
//       return '^';
//     }
//   }
  //
  // static List<TeXArg> _buildArgs(bool hasSubscript, bool hasSuperscript) {
  //   final args = <TeXArg>[];
  //   if (hasSubscript) args.add(TeXArg.subscript);
  //   if (hasSuperscript) args.add(TeXArg.superscript);
  //   return args;
  // }
//
//   static List<TeXNode> _buildArgNodes(
//       TeXNode? subscriptNode, TeXNode? superscriptNode) {
//     final nodes = <TeXNode>[];
//     if (subscriptNode != null) nodes.add(subscriptNode);
//     if (superscriptNode != null) nodes.add(superscriptNode);
//     return nodes;
//   }
//
//   @override
//   String buildString({Color? cursorColor}) {
//     final buffer = StringBuffer();
//
//     // Add base expression
//     buffer.write(baseNode.buildTeXString(cursorColor: cursorColor));
//
//     // Add subscript if present
//     if (hasSubscript) {
//       buffer.write('_{');
//       buffer.write(subscriptNode!.buildTeXString(cursorColor: cursorColor));
//       buffer.write('}');
//     }
//
//     // Add superscript if present
//     if (hasSuperscript) {
//       buffer.write('^{');
//       buffer.write(superscriptNode!.buildTeXString(cursorColor: cursorColor));
//       buffer.write('}');
//     }
//
//     return buffer.toString();
//   }
// }

/// Class for handling matrix structures.
class TeXMatrix extends TeXFunction {
  /// Constructs a [TeXMatrix].
  TeXMatrix(
    TeXNode parent,
    this.rows,
    this.columns, {
    String matrixType = 'pmatrix',
  })  : matrixType = matrixType,
        super(
          'matrix',
          parent,
          List.generate(rows * columns, (_) => TeXArg.matrixCell),
          _createCells(rows, columns),
        );

  /// Number of rows in the matrix.
  final int rows;

  /// Number of columns in the matrix.
  final int columns;

  /// Type of matrix (pmatrix, bmatrix, vmatrix, etc.).
  final String matrixType;

  /// Gets the matrix cells as a 2D structure.
  List<List<TeXNode>> get cells {
    final result = <List<TeXNode>>[];
    for (int i = 0; i < rows; i++) {
      final row = <TeXNode>[];
      for (int j = 0; j < columns; j++) {
        row.add(argNodes[i * columns + j]);
      }
      result.add(row);
    }
    return result;
  }

  /// Gets a specific cell by row and column indices.
  TeXNode getCell(int row, int col) {
    if (row < 0 || row >= rows || col < 0 || col >= columns) {
      throw ArgumentError('Cell position ($row, $col) is out of bounds');
    }
    return argNodes[row * columns + col];
  }

  /// Sets a specific cell by row and column indices.
  void setCell(int row, int col, TeXNode node) {
    if (row < 0 || row >= rows || col < 0 || col >= columns) {
      throw ArgumentError('Cell position ($row, $col) is out of bounds');
    }
    argNodes[row * columns + col] = node;
    node.parent = this;
  }

  static List<TeXNode> _createCells(int rows, int columns) {
    return List.generate(
      rows * columns,
      (index) => TeXNode(null),
    );
  }

  @override
  String buildString({Color? cursorColor}) {
    final buffer = StringBuffer('\\begin{$matrixType}');

    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < columns; j++) {
        buffer.write(getCell(i, j).buildTeXString(cursorColor: cursorColor));
        if (j < columns - 1) {
          buffer.write(' & ');
        }
      }
      if (i < rows - 1) {
        buffer.write(' \\\\ ');
      }
    }

    buffer.write('\\end{$matrixType}');
    return buffer.toString();
  }
}

/// Class for handling expressions with limits (integrals, sums, etc.).
class TeXLimitExpression extends TeXFunction {
  /// Constructs a [TeXLimitExpression].
  TeXLimitExpression(
    String symbol,
    TeXNode parent, {
    TeXNode? lowerLimit,
    TeXNode? upperLimit,
    TeXNode? bodyNode,
    bool hasBody = false,
  })  : hasBody = hasBody,
        super(
          symbol,
          parent,
          _buildArgs(lowerLimit != null, upperLimit != null, hasBody),
          _buildArgNodes(lowerLimit, upperLimit, bodyNode),
        );

  /// Whether this expression has a body (like integrals).
  final bool hasBody;

  /// Gets the lower limit node if it exists.
  TeXNode? get lowerLimit {
    if (argNodes.isEmpty) return null;
    return argNodes[0];
  }

  /// Gets the upper limit node if it exists.
  TeXNode? get upperLimit {
    if (argNodes.length < 2) return null;
    return argNodes[1];
  }

  /// Gets the body node if it exists.
  TeXNode? get bodyNode {
    if (!hasBody || argNodes.length < 3) return null;
    return argNodes[2];
  }

  /// Whether this has a lower limit.
  bool get hasLowerLimit => argNodes.isNotEmpty;

  /// Whether this has an upper limit.
  bool get hasUpperLimit => argNodes.length >= 2;

  static List<TeXArg> _buildArgs(bool hasLower, bool hasUpper, bool hasBody) {
    final args = <TeXArg>[];
    if (hasLower) args.add(TeXArg.limits);
    if (hasUpper) args.add(TeXArg.limits);
    if (hasBody) args.add(TeXArg.braces);
    return args;
  }

  static List<TeXNode> _buildArgNodes(
      TeXNode? lower, TeXNode? upper, TeXNode? body) {
    final nodes = <TeXNode>[];
    if (lower != null) nodes.add(lower);
    if (upper != null) nodes.add(upper);
    if (body != null) nodes.add(body);
    return nodes;
  }

  @override
  String buildString({Color? cursorColor}) {
    final buffer = StringBuffer(expression);

    // Add lower limit
    if (hasLowerLimit) {
      buffer.write('_{');
      buffer.write(lowerLimit!.buildTeXString(cursorColor: cursorColor));
      buffer.write('}');
    }

    // Add upper limit
    if (hasUpperLimit) {
      buffer.write('^{');
      buffer.write(upperLimit!.buildTeXString(cursorColor: cursorColor));
      buffer.write('}');
    }

    // Add body if present
    if (hasBody && bodyNode != null) {
      buffer.write(' ');
      buffer.write(bodyNode!.buildTeXString(cursorColor: cursorColor));
    }

    return buffer.toString();
  }
}
